package com.unitd.filesystem.client;

import com.alibaba.fastjson.JSON;
import com.unitd.filesystem.comm.fdfs.FdfsSetting;
import com.unitd.filesystem.provider.IFsProvider;
import com.unitd.filesystem.provider.impl.AliyunossProvider;
import com.unitd.filesystem.provider.impl.FdfsProvider;
import com.unitd.filesystem.provider.impl.QiniuProvider;
import com.unitd.frame.comm.utils.CharsetUtils;
import com.unitd.frame.spring.prop.ResourceUtils;
import org.apache.commons.lang3.Validate;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @desc 调用文件系统的对外抽象工厂类
 * 所有的需要调用第三方文件系统的都通过此工厂类来进行初始化(目前只支持fastDFS的存储服务,后续增加第三方存储服务如:七牛)
 * @filename FsClientFactory.java
 * @copyright www.unitd.com
 * @author Hudan
 * @version 1.0
 * @date 2017/3/6
 */
public class FsClientFactory {

	private FsClientFactory() {
	}

	/** 是否进行了初始化加载 */
	private static AtomicBoolean inited = new AtomicBoolean(false);
	private static Map<String, IFsProvider> fsProviders = new HashMap<>();

	/**
	 * @desc 读取配置文件,并将配置文件中定义的第三方文件服务方读取后,创建和初始化对应的文件服务提供者
	 * @param providerName 文件服务提供者(比如:fastDFS、七牛等)
	 * @param bucketName 文件服务中的服务组名
	 * @return 分布式文件服务提供者
	 */
	public static IFsProvider build(String providerName, String bucketName) {

		// 某个第三方文件服务的组
		String key = providerName + bucketName;

		if (!inited.get()) synchronized (FsClientFactory.class) {
			if (fsProviders.isEmpty()) {
				inited.set(true);

				// 第三方文件服务组,多个组名之间用,或者|或者;进行分割
				String groupNames = ResourceUtils.get("fs.groupNames");
				Validate.notBlank(groupNames, "[groupNames] not defined");
				String[] groups = groupNames.split(",|;");

				IFsProvider fsProvider = null;
				for (String group : groups) {

					// 文件服务提供者,目前只支持fastDFS的存储服务,此名称建议与IFSProvider的实现这中的NAME的值保持一直
					String provider = ResourceUtils.get("fs." + group + ".provider");
					if (!FdfsProvider.NAME.equals(provider) && !QiniuProvider.NAME.equals(provider)
							&& !AliyunossProvider.NAME.equals(provider)) {
						throw new RuntimeException("Provider[" + provider + "] not support");
					}

					String urlprefix = ResourceUtils.get("fs." + group + ".urlprefix");
					Validate.notBlank(urlprefix, "[urlprefix] not defined");

					/* fastDFS存储服务 */
					if (FdfsProvider.NAME.equals(provider)) {
						String trackServers = ResourceUtils.get("fs." + group + ".tracker_group");
						Validate.isTrue(trackServers != null && trackServers.matches("^.+[:]\\d{1,5}\\s*$"), "[servers] is not valid");

						FdfsSetting setting = new FdfsSetting();
						// 提供文件服务的文件系统服务名称
						setting.setProvider(provider);
						// 组名
						setting.setGroup(group);
						// fastDFS连接超时时长
						setting.setConnectTimeout(Integer.parseInt(ResourceUtils.get("fs." + group + ".connect_timeout", "2000")));
						// 网络超时时间
						setting.setNetworkTimeout(Integer.parseInt(ResourceUtils.get("fs." + group + ".network_timeout", "3000")));
						// 是否启用防盗链Token
						setting.setAntiStealToken(Boolean.parseBoolean(ResourceUtils.get("fs." + group + ".anti_steal_token", "false")));
						// 字符集(UTF-8/GBK等)
						setting.setCharset(ResourceUtils.get("fs." + group + ".charset", CharsetUtils.UTF_8.name()));
						// 密钥
						setting.setSecretKey(ResourceUtils.get("fs." + group + ".secret_key"));
						// fastDFS单次最大处理线程数
						setting.setMaxThreads(Integer.parseInt(ResourceUtils.get("fs." + group + ".maxThreads", "50")));
						//Tracker Server提供HTTP服务的端口
						setting.setTrackerHttpPort(Integer.parseInt(ResourceUtils.get("fs." + group + ".tracker_http_port", "7271")));
						// fastDFS的跟踪服务(Tracker Server)的地址及端口(多个服务之间用,分割;比如:92.168.118.12:22122;92.168.118.11:22122)
						setting.setTrackerGroup(strArr2NetAddress(trackServers));
						// 配置ngin代理和存储服务同步后的ngin地址地址为storage存储服务器对应的nginx地址; 比如:http://192.168.118.13:8888
						setting.setUrlprefix(urlprefix);

						// 初始化fastDFS服务提供者
						fsProvider = new FdfsProvider(urlprefix, setting);
						fsProviders.put(provider + group, fsProvider);
					}
					/** 七牛云存储 */
					else if(QiniuProvider.NAME.equals(provider)) {
						String accessKey = ResourceUtils.get("fs." + group + ".accessKey");
						String secretKey = ResourceUtils.get("fs." + group + ".secretKey");
						Validate.notBlank(accessKey, "[accessKey] not defined");
						Validate.notBlank(secretKey, "[secretKey] not defined");

						fsProvider = new QiniuProvider(urlprefix, group, accessKey, secretKey);
						fsProviders.put(provider + group, fsProvider);
					}
					/** 阿里云OSS文件系统 */
					else if(AliyunossProvider.NAME.equals(provider)) {
						String endpoint = ResourceUtils.get("fs." + group + ".urlprefix");
						String accessKey = ResourceUtils.get("fs." + group + ".accessKey");
						String secretKey = ResourceUtils.get("fs." + group + ".secretKey");
						Validate.notBlank(accessKey, "[accessKey] not defined");
						Validate.notBlank(secretKey, "[secretKey] not defined");

						fsProvider = new AliyunossProvider(endpoint, bucketName, accessKey, secretKey);
						fsProviders.put(provider + bucketName, fsProvider);
					}
				}
			}
		}
		return fsProviders.get(key);
	}

	/**
	 * @desc 关闭与所有的文件存储服务的链接
	 */
	public void closeAll() {
		Collection<IFsProvider> providers = fsProviders.values();
		for (IFsProvider p : providers) {
			try {
				p.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * @desc 将ip:port对应的网络地址字符串转换成java的网络地址对象
	 * @param trackServers ip:port地址串
	 * @return 网络地址数组
	 */
	private static InetSocketAddress[] strArr2NetAddress(String trackServers) {
		// 将文件服务地址字符串进行分组,获取多个服务ip:port对
		String[] trackArray = trackServers.split(",|;");

		// 定义服务网络地址对象
		int arrSize = trackArray.length;
		InetSocketAddress[] trackerGroups = new InetSocketAddress[arrSize];

		for(int i=0; i<arrSize; i++) {
			String[] server = trackArray[i].split(":");
			trackerGroups[i] = new InetSocketAddress(server[0], Integer.parseInt(server[1]));
		}
		return trackerGroups;
	}

	public static void main(String[] args) {
		String trackServers = "192.168.100.2:22122,192.168.23.33:22122";
		System.out.println(JSON.toJSONString(FsClientFactory.strArr2NetAddress(trackServers)));
	}
}